#include <container/spw_array.h>
#include <exception/spw_stdexcept.h>
#include <gtest/gtest.h>
#include <type_traits>
using namespace spwstd;
/* 
 *  ArrayTraits
 * */
TEST(ARRARY_TRAITS_TEST, NONZERO_NONCONST_INT) {
    using nonzero_nonconst_int_arr = spwarray::ArrayTraits<int, 3>::ArrType ;
    using nonzero_nonconst_int_struct = spwarray::ArrayTraits<int,3>;
    nonzero_nonconst_int_arr arr; // arr 是int[3];
    int sizearr = sizeof (arr);
    EXPECT_EQ(sizearr, 12);
    arr[0] = 1;
    arr[1] = 11;
    arr[2] = 111;
    // 测试ref, 
    // arr[1]=11
    EXPECT_EQ((nonzero_nonconst_int_struct::s_ref(arr, 1)), 11);
    // nonconst int
    EXPECT_FALSE(std::is_const<
            std::remove_reference_t<decltype(nonzero_nonconst_int_struct::s_ref(arr,1))>
            >::value);
    // 测试ptr
    EXPECT_TRUE(std::is_pointer<decltype(nonzero_nonconst_int_struct::s_ptr(arr))>::value);
    EXPECT_EQ((nonzero_nonconst_int_struct::s_ptr(arr)), arr);
    EXPECT_EQ(*((nonzero_nonconst_int_struct::s_ptr(arr))+1), 11);
}

TEST(ARRARY_TRAITS_TEST, NONZERO_CONST_INT) {
    using nonzero_const_int_arr = spwarray::ArrayTraits<const int, 3>::ArrType;
    using nonzero_const_int_struct = spwarray::ArrayTraits<const int, 3>;
    nonzero_const_int_arr arr = {1, 11, 111}; // arr 是const int[3];
    int sizearr = sizeof (arr);
    EXPECT_TRUE((std::is_const<nonzero_const_int_arr>::value));
    EXPECT_EQ(sizearr, 12);
    // 测试ref, 
    // arr[1]=11
    EXPECT_EQ((nonzero_const_int_struct::s_ref(arr, 1)), 11);
    // const int
    EXPECT_TRUE(std::is_const<nonzero_const_int_struct::ArrType>::value);

    EXPECT_TRUE(std::is_const<std::remove_reference_t<decltype(nonzero_const_int_struct::s_ref(arr,2))>>::value);

    // 测试ptr
    EXPECT_TRUE(std::is_pointer<decltype(nonzero_const_int_struct::s_ptr(arr))>::value);
    EXPECT_EQ((nonzero_const_int_struct::s_ptr(arr)), arr);
    EXPECT_EQ(*((nonzero_const_int_struct::s_ptr(arr))+1), 11);
}


TEST(ARRARY_TRAITS_TEST, ZERO_CONST_INT) {
    using zero_const_int_arr = spwarray::ArrayTraits<const int, 0>::ArrType;
    using zero_const_int_struct = spwarray::ArrayTraits<const int,0>;
    zero_const_int_arr arr;
// 调用ref会崩溃
    int a=1;
    EXPECT_DEATH((a = zero_const_int_struct::s_ref(arr, 2)), "");
    a = a+1;
// 调用ptr会返回nullptr
   EXPECT_TRUE(nullptr == zero_const_int_struct::s_ptr(arr));
   zero_const_int_struct::s_ref(arr, 0);
}

/* 
 * array type
 * */
// int
TEST(ARRARY_TYPE_TEST, INT) {
    using type_int = spwarray::Array<int,3>;
    EXPECT_TRUE((std::is_same<type_int::AtType::ArrType, int[3]>::value));
    EXPECT_FALSE((std::is_same<type_int::AtType::ArrType, int*>::value));
    // value_type = int
    EXPECT_TRUE((std::is_same<type_int::ValueType, int>::value));
    EXPECT_TRUE((std::is_same<type_int::Pointer, int*>::value));
    EXPECT_TRUE((std::is_same<type_int::ConstPointer, const int*>::value));
    EXPECT_TRUE((std::is_same<type_int::Refer, int&>::value));
    EXPECT_TRUE((std::is_same<type_int::ConstRefer, const int&>::value));
    EXPECT_TRUE((std::is_same<type_int::Iterator, int*>::value));
    EXPECT_TRUE((std::is_same<type_int::ConstIterator, const int*>::value));

    type_int arr{1,2,3};
    //+ 函数
    //- data
    // 调用data(), 返回内部数组的指针, 非const
    EXPECT_EQ(arr.Data(),arr.elems_);
    EXPECT_FALSE(std::is_const<decltype(arr.Data())>::value);
    EXPECT_EQ(*(arr.Data()+1), 2);
    // operator[]
    EXPECT_EQ(arr[2], 3);
    EXPECT_TRUE(std::is_reference<decltype(arr[2])>::value);
    EXPECT_FALSE(std::is_const<
            std::remove_reference<decltype(arr[2])>::type>::value);
    arr[2] = 5;
    EXPECT_EQ(arr[2], 5);
    // at
    EXPECT_EQ(arr[2], 5);
    EXPECT_THROW(arr.At(4), spwstd::spwexcept::OutOfRange);
}

// int*
TEST(ARRARY_TYPE_TEST, INT_POINTER) {
    using type_arr = spwarray::Array<int*,3>;
    EXPECT_TRUE((std::is_same<type_arr::AtType::ArrType, int*[3]>::value));
    // value_type = int
    using type_int_pointer = int*;
    EXPECT_TRUE((std::is_same<type_arr::ValueType, type_int_pointer>::value));
    EXPECT_TRUE((std::is_same<type_arr::Pointer, type_int_pointer*>::value));
    EXPECT_TRUE((std::is_same<type_arr::ConstPointer, const type_int_pointer*>::value));
    EXPECT_TRUE((std::is_same<type_arr::Refer, type_int_pointer&>::value));
    EXPECT_TRUE((std::is_same<type_arr::ConstRefer, const type_int_pointer &>::value));
    EXPECT_TRUE((std::is_same<type_arr::Iterator, type_int_pointer*>::value));
    EXPECT_TRUE((std::is_same<type_arr::ConstIterator, const type_int_pointer* >::value));
}

// const int
TEST(ARRARY_TYPE_TEST, CONST_INT) {
    using type_int = spwarray::Array<const int,3>;
    EXPECT_TRUE((std::is_same<type_int::AtType::ArrType, const int[3]>::value));
    // value_type = const int
    EXPECT_TRUE((std::is_same<type_int::ValueType,const int>::value));
    EXPECT_TRUE((std::is_same<type_int::Pointer, const int*>::value));
    EXPECT_TRUE((std::is_same<type_int::ConstPointer, const int*>::value));
    EXPECT_TRUE((std::is_same<type_int::Refer, const int&>::value));
    EXPECT_TRUE((std::is_same<type_int::ConstRefer, const int&>::value));
    EXPECT_TRUE((std::is_same<type_int::Iterator, const int*>::value));
    EXPECT_TRUE((std::is_same<type_int::ConstIterator, const int*>::value));
}
// const int*
TEST(ARRARY_TYPE_TEST, CONST_INT_POINTER) {
    using type_arr = spwarray::Array<const int*,3>;
    EXPECT_TRUE((std::is_same<type_arr::AtType::ArrType, const int*[3]>::value));
    // value_type = const int*
    using type_const_int_pointer = const int*;
    EXPECT_TRUE((std::is_same<type_arr::ValueType, type_const_int_pointer>::value));
    EXPECT_TRUE((std::is_same<type_arr::Pointer, type_const_int_pointer*>::value));
    EXPECT_TRUE((std::is_same<type_arr::ConstPointer, const type_const_int_pointer*>::value));
    EXPECT_TRUE((std::is_same<type_arr::Refer, type_const_int_pointer&>::value));
    EXPECT_TRUE((std::is_same<type_arr::ConstRefer, const type_const_int_pointer &>::value));
    EXPECT_TRUE((std::is_same<type_arr::Iterator, type_const_int_pointer*>::value));
    EXPECT_TRUE((std::is_same<type_arr::ConstIterator, const type_const_int_pointer* >::value));
}

// const int* const
TEST(ARRARY_TYPE_TEST, CONST_POINTER_INT_POINTER) {
    using type_arr = spwarray::Array<const int* const,3>;
    EXPECT_TRUE((std::is_same<type_arr::AtType::ArrType, const int* const[3]>::value));
    // value_type = const int* const
    using type_const_int_pointer_const = const int* const;
    EXPECT_TRUE((std::is_same<type_arr::ValueType, type_const_int_pointer_const>::value));
    EXPECT_TRUE((std::is_same<type_arr::Pointer, type_const_int_pointer_const*>::value));
    EXPECT_TRUE((std::is_same<type_arr::ConstPointer, const type_const_int_pointer_const*>::value));
    EXPECT_TRUE((std::is_same<type_arr::Refer, type_const_int_pointer_const&>::value));
    EXPECT_TRUE((std::is_same<type_arr::ConstRefer, const type_const_int_pointer_const &>::value));
    EXPECT_TRUE((std::is_same<type_arr::Iterator, type_const_int_pointer_const*>::value));
    EXPECT_TRUE((std::is_same<type_arr::ConstIterator, const type_const_int_pointer_const* >::value));
}

/* 函数
 * */
// ++ 元素访问
class ArrayTest: public ::testing::Test
{
public:
    ArrayTest(): arr_int_3{1,2,3}, arr_const_int_3{4,5,6}, arr_int_0{}, arr_char_3{'1', '2', '3'}, arr_double_3{1.0, 2.0, 3.0}
    { std::cout << "xxxxxxxxxxx  "<< __func__ << "  xxxxxxxxxxxxx  " << std::endl; }
    ~ArrayTest()
    {    std::cout << "xxxxxxxxxxx  "<< __func__ << "  xxxxxxxxxxxxx  " << std::endl; }

protected:
    static void SetUpTestCase() {
        std::cout << "xxxxxxxxxxx  "<< __func__ << "  xxxxxxxxxxxxx  " << std::endl;
    }

    static void TearDownTestCase() {
        std::cout << "xxxxxxxxxxx  " << __func__ << "  xxxxxxxxxx  " << std::endl;
    }

    void SetUp() override {
        std::cout << "xxxxxxxxxxx  " << __func__ << "  xxxxxxxxxxx  " << std::endl;
    }

    void TearDown() override {
        std::cout << "xxxxxxxxxxx  " << __func__ << "  xxxxxxxxxxx  " << std::endl;
    }

    spwarray::Array<int,3> arr_int_3;
    spwarray::Array<const int,3> arr_const_int_3;
    spwarray::Array<int,0> arr_int_0;
    spwarray::Array<char,3> arr_char_3;
    spwarray::Array<double,3> arr_double_3;
};

TEST_F(ArrayTest, IteratorTest) 
{
    // begin, end, cbegin, cend
    testing::StaticAssertTypeEq<decltype(arr_int_3.Begin()), int*>();
    testing::StaticAssertTypeEq<decltype(arr_int_3.End()), int*>();
    testing::StaticAssertTypeEq<decltype(arr_const_int_3.Begin()), const int*>();
    testing::StaticAssertTypeEq<decltype(arr_const_int_3.End()), const int*>();
    testing::StaticAssertTypeEq<decltype(arr_int_3.Cbegin()), const int*>();
    testing::StaticAssertTypeEq<decltype(arr_const_int_3.Cbegin()), const int*>();
    testing::StaticAssertTypeEq<decltype(arr_int_3.Cend()), const int*>();
    testing::StaticAssertTypeEq<decltype(arr_const_int_3.Cend()), const int*>();
    EXPECT_EQ(*arr_int_3.Begin(), 1);
    EXPECT_EQ(arr_int_3.End() - arr_int_3.Begin(), 3);
}

TEST_F(ArrayTest, ElemAccessTest) 
{
    //at
    EXPECT_EQ(arr_int_3.At(0), 1);
    EXPECT_EQ(arr_int_3.At(2), 3);
    EXPECT_THROW(arr_int_3.At(3), spwstd::spwexcept::OutOfRange);
    testing::StaticAssertTypeEq<decltype(arr_int_3.At(2)), int&>();
    testing::StaticAssertTypeEq<decltype(arr_const_int_3.At(2)),const int&>();


    EXPECT_THROW(arr_int_0.At(0), spwstd::spwexcept::OutOfRange);

    EXPECT_EQ(arr_char_3.At(0), '1');
    EXPECT_EQ(arr_char_3.At(2), '3');
    EXPECT_THROW(arr_char_3.At(3), spwstd::spwexcept::OutOfRange);
    testing::StaticAssertTypeEq<decltype(arr_char_3.At(2)), char&>();

    EXPECT_DOUBLE_EQ(arr_double_3.At(0), 1.0);
    EXPECT_DOUBLE_EQ(arr_double_3.At(2), 3.0);
    EXPECT_THROW(arr_double_3.At(3), spwstd::spwexcept::OutOfRange);
    testing::StaticAssertTypeEq<decltype(arr_double_3.At(2)), double&>();

    //operator[]
    EXPECT_EQ(arr_int_3[0], 1);
    EXPECT_EQ(arr_int_3[2], 3);
    EXPECT_NO_THROW(arr_int_3[3]);
    testing::StaticAssertTypeEq<decltype(arr_int_3[2]), int&>();
    testing::StaticAssertTypeEq<decltype(arr_const_int_3[2]),const int&>();


    EXPECT_NO_THROW(arr_int_0[0]);
    // 一旦使用arr_int_0[0], 就崩溃
    EXPECT_DEATH({
            if(arr_int_0[0] == 0){};
        }, "");

    EXPECT_EQ(arr_char_3[0], '1');
    EXPECT_EQ(arr_char_3[2], '3');
    EXPECT_NO_THROW(arr_char_3[3]);
    testing::StaticAssertTypeEq<decltype(arr_char_3[2]), char&>();

    EXPECT_DOUBLE_EQ(arr_double_3[0], 1.0);
    EXPECT_DOUBLE_EQ(arr_double_3[2], 3.0);
    EXPECT_NO_THROW(arr_double_3[3]);
    testing::StaticAssertTypeEq<decltype(arr_double_3[2]), double&>();

    //front
    EXPECT_EQ(arr_int_3.Front(), 1);
    EXPECT_EQ(arr_const_int_3.Front(), 4);
    EXPECT_EQ(arr_char_3.Front(), '1');
    EXPECT_DOUBLE_EQ(arr_double_3.Front(), 1);
    testing::StaticAssertTypeEq<decltype(arr_int_3.Front()), int&>();
    testing::StaticAssertTypeEq<decltype(arr_const_int_3.Front()), const int&>();
    //back
    EXPECT_EQ(arr_int_3.Back(), 3);
    EXPECT_EQ(arr_const_int_3.Back(), 6);
    EXPECT_EQ(arr_char_3.Back(), '3');
    EXPECT_DOUBLE_EQ(arr_double_3.Back(), 3);
    testing::StaticAssertTypeEq<decltype(arr_int_3.Back()), int&>();
    testing::StaticAssertTypeEq<decltype(arr_const_int_3.Back()), const int&>();
    //data
    EXPECT_TRUE(*arr_int_3.Data() == 1) << *arr_int_3.Data();
    testing::StaticAssertTypeEq<decltype(arr_int_3.Data()), int*>();
    testing::StaticAssertTypeEq<decltype(arr_const_int_3.Data()), const int*>();
    EXPECT_TRUE(arr_int_0.Data() == nullptr);
}

TEST_F(ArrayTest, CapacityTest) 
{
    // size, empty 
    EXPECT_EQ(arr_int_3.Size(), 3ul);
    EXPECT_EQ(arr_int_0.Size(), 0ul);
    EXPECT_TRUE(arr_int_0.Empty());
    EXPECT_TRUE(!arr_int_3.Empty());

}

TEST_F(ArrayTest, OperatorTest) 
{
    //fill
    arr_int_3.Fill(0);
    for(size_t i=0; i < arr_int_3.Size(); ++i)
    { EXPECT_EQ(arr_int_3[i], 0); }
    arr_int_0.Fill(1);
    //swap
    spwstd::spwarray::Array<int, 3> a2{0,9,8};
    a2.Swap(arr_int_3);
    EXPECT_EQ(a2[0], 0);
    EXPECT_EQ(a2[1], 0);
    EXPECT_EQ(a2[2], 0);
    EXPECT_EQ(arr_int_3[0], 0);
    EXPECT_EQ(arr_int_3[1], 9);
    EXPECT_EQ(arr_int_3[2], 8);

    spwstd::spwarray::Array<int,0> a3;
    a3.Swap(arr_int_0);
}
